package com.chancein.cloud.ocr.process.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.chancein.cloud.ocr.process.domain.CellData;
import com.chancein.cloud.ocr.process.domain.OcrExcelData;
import com.chancein.cloud.ocr.process.domain.RowHeight;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * 百度OCR工具类
 *
 * @author: LiaoZiHao
 * @date: 2023/8/9 19:43
 * Copyright © 2022 Hundsun Technologies Inc. All Rights Reserved
 */
@Slf4j
public class OcrUtils {

    //private static final String ocrServerUrl = "http://10.5.23.190:8866/predict/ocr_system";
    private static final String ocrServerUrl = "http://10.5.23.199:9998/ocr/prediction";

    public static String imageAnalyticalStream(InputStream imageStream) {
        String imageInfo = null;

        //创建请求头
        HttpHeaders headers = new HttpHeaders();
        //设置请求头格式
        headers.setContentType(MediaType.APPLICATION_JSON);

        //构建请求参数
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();

        //构建请求
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        //发送请求
        try {
            //添加请求参数images，并将Base64编码的图片传入
            map.add("key", "image");
            map.add("value", ImageBase64Utils.imageToBase64(imageStream));
            ResponseEntity<JSONObject> response = restTemplate.postForEntity(ocrServerUrl, request, JSONObject.class);
            if (response.getStatusCode().is2xxSuccessful()) {
                imageInfo = JSONObject.toJSONString(response.getBody());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                imageStream.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        return imageInfo;
    }
    /**
     * 通过解析数据转换为二维列表
     *
     * @param jsonStr json返回数据
     * @return OcrExcelData
     */
    public static OcrExcelData getExcelData(String jsonStr) {

        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }

        JSONObject json = JSONObject.parseObject(jsonStr);
        Integer status;

        // PaddleOCR-v4 返回码解析
        if (json.containsKey("err_no")) {
            status = json.getInteger("err_no");
        } else {
            status = json.getInteger("status");
        }
        if (status != 0) {
            return null;
        }

        List<List<String>> excelList = new ArrayList<>();
        List<List<CellData>> itemList = new ArrayList<>();

        JSONArray jsonArray;
        JSONArray ja;
        if (CollectionUtils.isEmpty(json.getJSONArray("results"))) {
            // PaddleOCR-v4 返回结果解析
            JSONObject formattedJson = Ppocrv4ResponseJsonFormat(json.getString("value"));
            ja = formattedJson.getJSONArray("value");
        } else {
            // 常规返回结果解析
            jsonArray = json.getJSONArray("results");
            ja = (JSONArray) jsonArray.get(0);
        }
        if (!ja.isEmpty()) {
            Integer y = null;
            Integer wordHeight = null;
            List<CellData> itemRowList = new ArrayList<>();
            for (int i = 0; i < ja.size(); i++) {
                CellData item = ja.getObject(i, CellData.class);

                List<List<Integer>> pos = item.getTextRegion();

                List<Integer> rightBottom = pos.get(2);
                List<Integer> rightTop = pos.get(1);

                if (y != null) {
                    // 判断是否需要换行
                    if (rightTop.get(1) >= y + wordHeight / 2) {
                        itemList.add(itemRowList);
                        itemRowList = new ArrayList<>();

                        // 计算当前文字高度
                        wordHeight = rightBottom.get(1) - rightTop.get(1);
                        // 当前文字Y轴
                        y = rightTop.get(1);
                    }
                } else {
                    // 计算当前文字高度
                    wordHeight = rightBottom.get(1) - rightTop.get(1);
                    // 当前文字Y轴
                    y = rightTop.get(1);
                }
                itemRowList.add(item);
            }

            // 添加最后一行
            if (!itemRowList.isEmpty()) {
                itemList.add(itemRowList);
            }
        }

        // 获取最在列数据
        List<CellData> maxItem = null;
        // 行高数据
        List<RowHeight> rowHeightList = new ArrayList<>();

        int itemLength = itemList.size();
        // 列排序
        for (int j = 0; j < itemLength; j++) {
            List<CellData> item = itemList.get(j);
            item.sort((o1, o2) -> o1.getTextRegion().get(0).get(0) - o2.getTextRegion().get(0).get(0));
            int rowSize = item.size();

            boolean checkMaxRow = true;
            if (j == 0) {
                String firstName = item.get(0).getText();
                if (rowSize == 1 || firstName.startsWith("图") || firstName.startsWith("表")) {
                    checkMaxRow = false;
                }
            } else if (j == itemLength - 1) {
                String remark = item.get(0).getText();

                if (rowSize == 1 || remark.contains("来源")) {
                    checkMaxRow = false;
                }
            }

            if (checkMaxRow) { // 非标题及来源校验宽度
                if (maxItem == null || maxItem.size() < rowSize) { // 数量对比
                    maxItem = item;
                } else if (maxItem.size() == rowSize &&
                        maxItem.get(maxItem.size() - 1).getTextRegion().get(1).get(0) < item.get(rowSize - 1).getTextRegion().get(1).get(0)) {
                    // 宽度对比
                    maxItem = item;
                }
            }

            // 设置行最大高度差坐标
            RowHeight rh = new RowHeight();
            for (CellData cd : item) {
                List<List<Integer>> rowRegion = cd.getTextRegion();
                int topY = rowRegion.get(1).get(1);
                int bottomY = rowRegion.get(2).get(1);

                if (topY < rh.getTopY() || rh.getTopY() == 0) {
                    rh.setTopY(topY);
                }
                if (bottomY < rh.getBottomY() || rh.getBottomY() == 0) {
                    rh.setBottomY(bottomY);
                }
            }
            rowHeightList.add(rh);
        }

        int maxAvrHeight = 0;
        int maxRowNum = 0;
        if (maxItem != null) {
            // 计算最大行平均高度
            List<List<Integer>> maxFirstRegion = maxItem.get(0).getTextRegion();
            maxAvrHeight = maxFirstRegion.get(2).get(1) - maxFirstRegion.get(1).get(1);
            maxRowNum = maxItem.size();
        }

        OcrExcelData ocrData = new OcrExcelData();
        List<List<CellData>> newItemList = new ArrayList<>();

        // 填充空白表格数据
        for (int j = 0; j < itemLength; j++) {

            List<CellData> rowList = itemList.get(j);
            int rowSize = rowList.size();

            if (j == 0) { // 首行判断是否为标题
                String firstName = rowList.get(0).getText();
                if (rowSize == 1 || firstName.startsWith("图") || firstName.startsWith("表")) {
                    // 记录标题行
                    for (CellData c : rowList) {
                        if (ocrData.getTitle() == null) {
                            ocrData.setTitle(c.getText());
                        } else {
                            ocrData.setTitle(ocrData.getTitle() + c.getText());
                        }
                    }
                    continue;
                }
            } else if (j == itemLength - 1) { // 尾行判断是否为备注

                String remark = rowList.get(0).getText();

                if (rowSize == 1 || remark.contains("来源")) {
                    // 记录备注行
                    for (CellData c : rowList) {
                        if (ocrData.getRemark() == null) {
                            ocrData.setRemark(c.getText());
                        } else {
                            ocrData.setRemark(ocrData.getRemark() + c.getText());
                        }
                    }

                    continue;
                }
            }

            List<CellData> newRowList = CloneUtils.cloneList(rowList);

            if (maxRowNum > 0 && rowSize < maxRowNum) {

                // 检查合并行
                if (j > 1 && newItemList.size() > 0) {
                    RowHeight curRh = rowHeightList.get(j);
                    RowHeight prevRh = rowHeightList.get(j - 1);
                    if (prevRh.getBottomY() - curRh.getTopY() > 5) { // 向上合并

                        // 获取上行数据进行对比
                        List<CellData> perRowList = newItemList.get(newItemList.size() - 1);
                        int addSize = 0;
// ------------------------------------------- 根据行高合并处理 ------------------------------------------->>
// 根据行高进行合并,特殊数据会导致数据合并,处理数据不方便
//                        for (int s = 0; s < perRowList.size(); s ++) {
//                            CellData pre = perRowList.get(s);
//                            int preMiddleX = (pre.getTextRegion().get(1).get(0) + pre.getTextRegion().get(0).get(0)) / 2;
//
//                            for (int i = addSize; i < rowSize; i ++) {
//                                CellData cellData = rowList.get(i);
//                                int middleX = (cellData.getTextRegion().get(1).get(0) + cellData.getTextRegion().get(0).get(0)) / 2;
//                                if (middleX <= preMiddleX + maxAvrHeight/2) {
//                                    pre.setContent(pre.getContent() + cellData.getContent());
//                                    addSize ++;
//                                }
//                            }
//
//                            if(addSize >= rowSize) {
//                                break;
//                            }
//                        }
//
//                        // 未对比成功数据合并
//                        if (addSize < rowSize) {
//                            for (int i = addSize; i < rowSize; i ++) {
//                                CellData cellData = rowList.get(i);
//                                CellData pre = perRowList.get(maxRowNum - rowSize + addSize);
//                                pre.setContent(pre.getContent() + cellData.getContent());
//                                addSize ++;
//                            }
//                        }
//
//                        // 更新上一行数据
//                        newItemList.set(newItemList.size() - 1, perRowList);
//                        // 设置底部Y轴
//                        prevRh.setBottomY(curRh.getBottomY());
// ------------------------------------------- 根据行高合并处理 -------------------------------------------<<

// ------------------------------------------- 行填空式合并处理 ------------------------------------------->>
                        boolean canMerge = true;
                        List<CellData> newPreRowList = CloneUtils.cloneList(perRowList);
                        // 填空白合并
                        for (int s = 0; s < perRowList.size(); s++) {
                            CellData pre = perRowList.get(s);
                            int preMiddleX = (pre.getTextRegion().get(1).get(0) + pre.getTextRegion().get(0).get(0)) / 2;

                            for (int i = addSize; i < rowSize; i++) {
                                CellData cellData = rowList.get(i);
                                int middleX = (cellData.getTextRegion().get(1).get(0) + cellData.getTextRegion().get(0).get(0)) / 2;
                                if (middleX <= preMiddleX + maxAvrHeight / 2) {
                                    if (!StringUtils.isEmpty(pre.getText())) {
                                        canMerge = false;
                                        break;
                                    }
                                    CellData newPre = newPreRowList.get(s);
                                    newPre.setText(cellData.getText());
                                    addSize++;
                                }
                            }

                            if (!canMerge || addSize >= rowSize) {
                                break;
                            }
                        }

                        // 未对比成功数据合并
                        if (addSize < rowSize) {
                            for (int i = addSize; i < rowSize; i++) {
                                CellData cellData = rowList.get(i);
                                CellData pre = newPreRowList.get(maxRowNum - rowSize + addSize);
                                if (!StringUtils.isEmpty(pre.getText())) {
                                    canMerge = false;
                                    break;
                                }
                                pre.setText(pre.getText() + cellData.getText());
                                addSize++;
                            }
                        }

                        if (canMerge) {
                            // 更新上一行数据
                            newItemList.set(newItemList.size() - 1, newPreRowList);
                            continue;
                        }
// ------------------------------------------- 行填空式合并处理 -------------------------------------------<<
                    }

                }

                // 计算需要填充的空白数量
                int toAddSize = maxRowNum - rowSize;
                // 当前行已添加的空白数量
                int addSize = 0;

                for (int i = 0; i < rowSize; i++) {
                    CellData cellData = rowList.get(i);
                    int x = cellData.getTextRegion().get(0).get(0);
                    for (int s = i + addSize; s < maxItem.size(); s++) {
                        CellData max = maxItem.get(s);
                        int maxX = max.getTextRegion().get(0).get(0);

                        if (addSize >= toAddSize) {
                            break;
                        }

                        if (x <= maxX + maxAvrHeight / 2) {
                            break;
                        }

                        CellData data = new CellData();
                        data.setText("");
                        data.setTextRegion(max.getTextRegion());
                        newRowList.add(i + addSize, data);
                        addSize++;
                    }

                    if (addSize >= toAddSize) {
                        break;
                    }
                }

                // 向后填充
                for (int i = 0; i < toAddSize - addSize; i++) {
                    CellData data = new CellData();
                    data.setText("");
                    data.setTextRegion(maxItem.get(newRowList.size() - 1).getTextRegion());
                    newRowList.add(data);
                }

            }

            newItemList.add(newRowList);

        }

        for (List<CellData> newRowList : newItemList) {
            List<String> rowTextList = new ArrayList<>();
            for (CellData newRow : newRowList) {
                rowTextList.add(newRow.getText());
            }
            excelList.add(rowTextList);
        }

        // 添加来源数据至末尾
        if (ocrData.getRemark() != null && !ocrData.getRemark().isEmpty()) {
            List<String> rowTextList = new ArrayList<>();
            rowTextList.add(ocrData.getRemark());
            for (int i = 0; i < maxRowNum - 1; i++) {
                rowTextList.add("");
            }
            excelList.add(rowTextList);
        }

        ocrData.setDataList(excelList);

        return ocrData;
    }

    public static JSONObject Ppocrv4ResponseJsonFormat(String resJson) {
        // 去掉数组转String后出现在句首尾的 [""]
        if (resJson.startsWith("[\"") && resJson.endsWith("\"]")) {
            resJson = resJson.substring(2, resJson.length() - 2);
        }
        // 去除返回结果中不符合Json格式的字符
        String formattedJson = resJson.replaceAll("[()]", "").replace("'", "\"");
        // 转换为JSONArray
        JSONArray initArray = JSONArray.parseArray(formattedJson);
        JSONArray valueArray = new JSONArray();
        for (Object obj : initArray) {
            JSONArray item1 = (JSONArray) obj;
            String text = (String) item1.get(0);
            BigDecimal confidence = (BigDecimal) item1.get(1);
            // 创建text_region数组的区域
            JSONArray regionArray = new JSONArray();
            // 为了适配其他OCR组件，需要将PPOCR-V4返回的float数据转化为int数据
            JSONArray regionJsonArray = (JSONArray) item1.get(2);
            for (Object region : regionJsonArray) {
                if (region instanceof JSONArray) {
                    // 处理嵌套的JSONArray
                    JSONArray nestedArray = (JSONArray) region;
                    Integer[] nestedInts = new Integer[nestedArray.size()];
                    for (int i = 0; i < nestedArray.size(); i++) {
                        // fastJson中默认将JSON的小数值设置为BigDecimal类型
                        nestedInts[i] = ((BigDecimal) nestedArray.get(i)).intValue();
                    }
                    regionArray.add(nestedInts);
                } else {
                    // 单个BigDecimal的情况
                    Integer regionInt = ((BigDecimal) region).intValue();
                    regionArray.add(regionInt);
                }
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("confidence", confidence);
            jsonObject.put("text", text);
            jsonObject.put("text_region", regionArray);
            valueArray.add(jsonObject);
        }
        JSONObject result = new JSONObject();
        result.put("value", valueArray);
        return result;
    }
}
